/* Backwards compatibility */
options.enable_uncompressed_cache = TRUE;
+ g_auto(OstreeRepoMemoryCacheRef) memcache_ref;
+ _ostree_repo_memory_cache_ref_init (&memcache_ref, self);
return checkout_tree_at (self, &options,
AT_FDCWD, gs_file_get_path_cached (destination),
source, source_info,
if (!target_info)
return FALSE;
+ g_auto(OstreeRepoMemoryCacheRef) memcache_ref;
+ _ostree_repo_memory_cache_ref_init (&memcache_ref, self);
if (!checkout_tree_at (self, options,
destination_dfd,
destination_path,
OstreeRepoTransactionStats txn_stats;
GMutex cache_lock;
+ guint dirmeta_cache_refcount;
+ /* char * checksum → GVariant * for dirmeta objects, used in the checkout path */
+ GHashTable *dirmeta_cache;
gboolean inited;
gboolean writable;
char checksum[OSTREE_SHA256_STRING_LEN+1];
} OstreeDevIno;
+/* A MemoryCacheRef is an in-memory cache of objects (currently just DIRMETA). This can
+ * be used when performing an operation that traverses a repository in someway. Currently,
+ * the primary use case is ostree_repo_checkout_at() avoiding lots of duplicate dirmeta
+ * lookups.
+ */
+typedef struct {
+ OstreeRepo *repo;
+} OstreeRepoMemoryCacheRef;
+
+
+void
+_ostree_repo_memory_cache_ref_init (OstreeRepoMemoryCacheRef *state,
+ OstreeRepo *repo);
+
+void
+_ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state);
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(OstreeRepoMemoryCacheRef, _ostree_repo_memory_cache_ref_destroy);
+
#define OSTREE_REPO_TMPDIR_STAGING "staging-"
#define OSTREE_REPO_TMPDIR_FETCHER "fetcher-"
g_clear_pointer (&self->txn_refs, g_hash_table_destroy);
g_clear_error (&self->writable_error);
g_clear_pointer (&self->object_sizes, (GDestroyNotify) g_hash_table_unref);
+ g_clear_pointer (&self->dirmeta_cache, (GDestroyNotify) g_hash_table_unref);
g_mutex_clear (&self->cache_lock);
g_mutex_clear (&self->txn_stats_lock);
g_return_val_if_fail (OSTREE_OBJECT_TYPE_IS_META (objtype), FALSE);
+ /* Special caching for dirmeta objects, since they're commonly referenced many
+ * times.
+ */
+ const gboolean is_dirmeta_cachable =
+ (objtype == OSTREE_OBJECT_TYPE_DIR_META && out_variant && !out_stream);
+ if (is_dirmeta_cachable)
+ {
+ GMutex *lock = &self->cache_lock;
+ g_mutex_lock (lock);
+ GVariant *cache_hit = NULL;
+ /* Look it up, if we have a cache */
+ if (self->dirmeta_cache)
+ cache_hit = g_hash_table_lookup (self->dirmeta_cache, sha256);
+ if (cache_hit)
+ *out_variant = g_variant_ref (cache_hit);
+ g_mutex_unlock (lock);
+ if (cache_hit)
+ return TRUE;
+ }
+
_ostree_loose_path (loose_path_buf, sha256, objtype, self->mode);
if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd,
data, TRUE);
g_variant_ref_sink (ret_variant);
}
+
+ /* Now, let's put it in the cache */
+ if (is_dirmeta_cachable)
+ {
+ GMutex *lock = &self->cache_lock;
+ g_mutex_lock (lock);
+ if (self->dirmeta_cache)
+ g_hash_table_replace (self->dirmeta_cache, g_strdup (sha256), g_variant_ref (ret_variant));
+ g_mutex_unlock (lock);
+ }
}
else if (out_stream)
{
out:
return ret;
}
+
+/* See ostree-repo-private.h for more information about this */
+void
+_ostree_repo_memory_cache_ref_init (OstreeRepoMemoryCacheRef *state,
+ OstreeRepo *repo)
+{
+ state->repo = g_object_ref (repo);
+ GMutex *lock = &repo->cache_lock;
+ g_mutex_lock (lock);
+ repo->dirmeta_cache_refcount++;
+ if (repo->dirmeta_cache == NULL)
+ repo->dirmeta_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
+ g_mutex_unlock (lock);
+
+}
+
+/* See ostree-repo-private.h for more information about this */
+void
+_ostree_repo_memory_cache_ref_destroy (OstreeRepoMemoryCacheRef *state)
+{
+ OstreeRepo *repo = state->repo;
+ GMutex *lock = &repo->cache_lock;
+ g_mutex_lock (lock);
+ repo->dirmeta_cache_refcount--;
+ if (repo->dirmeta_cache_refcount == 0)
+ g_clear_pointer (&repo->dirmeta_cache, (GDestroyNotify) g_hash_table_unref);
+ g_mutex_unlock (lock);
+ g_object_unref (repo);
+}